<?php
//=======================================
//###################################
// Kayako Web Solutions
//
// Source Copyright 2001-2004 Kayako Infotech Ltd.
// Unauthorized reproduction is not allowed
// License Number: $%LICENSE%$
// $Author: vshoor $ ($Date: 2007/07/17 06:42:45 $)
// $RCSfile: functions_mime.php,v $ : $Revision: 1.19 $ 
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//###################################
//=======================================



require_once ("./includes/MIME/mimeDecode.php");
require_once ("./includes/MIME/RFC822.php");

/**
* Class to Parse MIME Data
*/
class MimeParser
{
	/*
	* Constructor
	*/
	function MimeParser()
	{
		global $decodeMail_output, $decodeMail_inRelated;

		$decodeMail_output = new stdClass;
		$decodeMail_inRelated = false;
	}

	/**
	* Decodes a mail and returns the various parts.
	* Original Function by Richard Heyes, Modified to fix some bugs
	*/
	function decodeMail($input)
	{
		global $decodeMail_output, $_SWIFT, $charset;

		// Declare the additional classes
//		parserDebug("MIME: Initiating Classes");
		$Mail_mimeDecode = new Mail_mimeDecode($input, SWIFT_CRLF);
		$Mail_RFC822 = new Mail_RFC822;

		$output = &$decodeMail_output;
//		parserDebug("MIME: Got Output");

		$decode_params['input'] = $input;
		$decode_params['include_bodies'] = true;
		$decode_params['decode_bodies']  = true;
		$decode_params['decode_headers'] = true;
//		parserDebug("MIME: Input Is\n".$input);
//		parserDebug("MIME: Decoding...");
		$structure = $Mail_mimeDecode->decode($decode_params);
//		parserDebug("MIME: Traversing Structure");
		$this->decodeMail_traverseStructure($structure);
		
		// Sizes
		$output->textSize = strlen(@$output->text);
		$output->htmlSize = strlen(@$output->html);
//		parserDebug("MIME: Text Size: ".$output->textSize.", HTML Size: ".$output->htmlSize);

		// Headers
		$output->headers = $structure->headers;
//		parserDebug("MIME: Got Headers");

		$tofix = array("\(", "\)", "\[", "\]", "\{", "\}");
		$fixres = array("(", ")", "[", "]", "{", "}");

		// From name/email
		if (!empty($output->headers['from'])) {
			$from = $Mail_RFC822->parseAddressList(str_replace($tofix, $fixres, $output->headers['from']), null, null, false);
			$output->fromEmail = sprintf('%s@%s', $from[0]->mailbox, $from[0]->host);
			$output->fromName  = preg_replace('/^"(.*)"$/', '\1', $from[0]->personal);
		}
//		parserDebug("MIME: Parsed From");

		// Reply to
		if (!empty($output->headers['reply-to'])) {
			$replyto = $Mail_RFC822->parseAddressList(str_replace($tofix, $fixres, $output->headers['reply-to']), null, null, false);
			$output->replytoEmail = sprintf('%s@%s', $replyto[0]->mailbox, $replyto[0]->host);
			$output->replytoName = preg_replace('/^"(.*)"$/', '\1', $replyto[0]->personal);
		}
//		parserDebug("MIME: Parsed Reply To");

		// To name/email
		if (!empty($output->headers['to'])) {
			$to = $Mail_RFC822->parseAddressList(str_replace($tofix, $fixres, $output->headers['to']), null, null, false);
			if (is_array($output->headers['to'])) {
				$output->toEmail = $output->headers['to'][0];
			} else {
				$output->toEmail = sprintf('%s@%s', $to[0]->mailbox, $to[0]->host);
			}
			$output->toName = preg_replace('/^"(.*)"$/', '\1', $to[0]->personal);
		}
//		parserDebug("MIME: Parsed To");

		// In-Reply-To
		if (!empty($output->headers['in-reply-to'])) {
			$inreplyto = $Mail_RFC822->parseAddressList(str_replace($tofix, $fixres, $output->headers['in-reply-to']), null, null, false);
			$output->inReplyTo = trim($inreplyto[0]->mailbox);
		}
//		parserDebug("MIME: Parsed In-Reply-To");

		// Return address
		if (!empty($output->headers['reply-to'])) {
			$output->returnAddress = $output->headers['reply-to'];
		} elseif (!empty($output->headers['sender'])) {
			$output->returnAddress = $output->headers['sender'];
		} elseif (!empty($output->headers['from'])) {
			$output->returnAddress = $output->headers['from'];
		} elseif (!empty($output->headers['return-path'])) {
			$output->returnAddress = $output->headers['return-path'];
		}
		if (!empty($output->returnAddress)) {
			$retAddress = $Mail_RFC822->parseAddressList(str_replace($tofix, $fixres, $output->returnAddress), null, null, false);
			$output->returnAddressEmail = sprintf('%s@%s', $retAddress[0]->mailbox, $retAddress[0]->host);
			$output->returnAddressName = preg_replace('/^"(.*)"$/', '\1', $retAddress[0]->personal);
		}
//		parserDebug("MIME: Calculated the Return Address: ".$output->returnAddress);

		// Recipient addresses
		$shiftcount = 0;
		foreach (array('to', 'cc', 'x-rcpt-to') as $header) {
			if (!empty($output->headers[$header]) && !is_array($output->headers['to'])) {
				$to = $Mail_RFC822->parseAddressList(str_replace($tofix, $fixres, $output->headers[$header]), null, null, false);
				$loopcount = count($to);
				if ($loopcount > 10)
				{
					$loopcount = 10;
				}
				for ($i=0; $i<$loopcount; $i++) {
					if ($to[$i]) {
						$recipientemail = sprintf('%s@%s', $to[$i]->mailbox, $to[$i]->host);
						if ($this->isValidEmail($recipientemail))
						{
							$recipients[] = $recipientemail;
						}
					}
				}
			} else {
				// Most probably this email has multiple to's
				if ($this->isValidEmail($output->headers['to'][0]))
				{
					$recipients[] = $output->headers['to'][0];
				}
			}
			$shiftcount++;

			if ($shiftcount > 50)
			{
				break;
			}
		}

		$output->recipientAddresses = $recipients;

		//$charset = $structure->ctype_parameters['charset'];
		$subjectUtf8 = $output->headers["subject"];
		if ($_SWIFT["settings"]["pr_conversion"] == 1 && extension_loaded("mbstring"))
		{
			if (empty($charset))
			{
				$_emailcharset = mb_detect_encoding($output->headers["subject"]);
			} else {
				$_emailcharset = $charset;
			}

			if (!empty($_emailcharset))
			{
				$subjectUtf8 = @mb_convert_encoding($subjectUtf8, $_SWIFT["language"]["charset"], $_emailcharset);
				$_fromName = @mb_convert_encoding($output->fromName, $_SWIFT["language"]["charset"], $_emailcharset);
				$_replytoName = @mb_convert_encoding($output->replytoName, $_SWIFT["language"]["charset"], $_emailcharset);
				$_toName = @mb_convert_encoding($output->toName, $_SWIFT["language"]["charset"], $_emailcharset);
				$output->fromName = iif(empty($_fromName), $output->fromName, $_fromName);
				$output->replytoName = iif(empty($_replytoName), $output->replytoName, $_replytoName);
				$output->toName = iif(empty($_toName), $output->toName, $_toName);
				if (empty($subjectUtf8))
				{
					$subjectUtf8 = $output->headers["subject"];
				}
			}

		} else if ($_SWIFT["language"]["charset"] == "UTF-8") {
			if (!extension_loaded('mbstring') && function_exists("utf8_encode")) {
				if (strtolower($charset) != "utf-8") {
					$subjectUtf8 = utf8_encode($subjectUtf8);
					$output->fromName = utf8_encode($output->fromName);
					$output->replytoName = utf8_encode($output->replytoName);
					$output->toName = utf8_encode($output->toName);
				}
			} else if (extension_loaded("mbstring")) {
				$subjectUtf8 = @mb_convert_encoding($subjectUtf8, "UTF-8", $charset);
				$_fromName = @mb_convert_encoding($output->fromName, "UTF-8", $charset);
				$_replytoName = @mb_convert_encoding($output->replytoName, "UTF-8", $charset);
				$_toName = @mb_convert_encoding($output->toName, "UTF-8", $charset);
				$output->fromName = iif(empty($_fromName), $output->fromName, $_fromName);
				$output->replytoName = iif(empty($_replytoName), $output->replytoName, $_replytoName);
				$output->toName = iif(empty($_toName), $output->toName, $_toName);
				if (empty($subjectUtf8))
				{
					$subjectUtf8 = $output->headers["subject"];
				}
			}
		}
		$output->subject = $subjectUtf8;
//		parserDebug("MIME: Got the Subject: ".$output->subject);

		return $output;
	}

	/**
	* Traverses the decoded structure and adds the various
	* parts to the global var.
	*/
	function decodeMail_traverseStructure($structure)
	{
		global $decodeMail_output, $decodeMail_inRelated, $_SWIFT, $charset;

		$output = &$decodeMail_output;
		$ctype  = strtolower(@$structure->ctype_primary) . '/' . strtolower(@$structure->ctype_secondary);
		if (!$ctype) {
			$ctype = 'text/plain';
		}

		switch ($ctype) {
			case 'text/html':
			case 'text/plain':
				$charset = "us-ascii"; // Default charset, see RFC 2045
				if (!empty($structure->ctype_parameters) AND isset($structure->ctype_parameters['charset'])) {
					$charset = $structure->ctype_parameters['charset'];
				}
				if (!empty($structure->disposition) AND $structure->disposition == 'attachment') {
					$this->decodeMail_addAttachment($structure);
				} else {
					$var = $ctype == 'text/html' ? 'html' : 'text';
					$bodyUtf8 = $structure->body;

					if ($_SWIFT["settings"]["pr_conversion"] == 1 && extension_loaded("mbstring"))
					{
						if (empty($charset))
						{
							$_emailcharset = mb_detect_encoding($output->headers["subject"]);
						} else {
							$_emailcharset = $charset;
						}

						if (!empty($_emailcharset))
						{
							$bodyUtf8 = @mb_convert_encoding($bodyUtf8, $_SWIFT["language"]["charset"], $_emailcharset);
							if (empty($bodyUtf8))
							{
								$bodyUtf8 = $structure->body;
							}
						}
					} else if ($_SWIFT["language"]["charset"] == "UTF-8") {
						if (!extension_loaded('mbstring') && function_exists("utf8_encode")) {
							if (strtolower($charset) != "utf-8") {
								$bodyUtf8 = utf8_encode($bodyUtf8);
							}
						} else if (extension_loaded("mbstring")) {
							$bodyUtf8 = @mb_convert_encoding($bodyUtf8, "UTF-8", $charset);
							if (empty($bodyUtf8))
							{
								$bodyUtf8 = $structure->body;
							}
						}
					}
					@$output->$var .= $bodyUtf8;
				}
				break;
			case 'multipart/report':
			case 'multipart/signed':
			case 'multipart/mixed':
			case 'multipart/alternative':
			case 'multipart/related':
			case 'multipart/digest':
			case 'multipart/parallel':
			case 'message/rfc822':
			case 'message/disposition-notification':
				if (!empty($structure->parts)) {
					if ($ctype == 'multipart/related') {
						$decodeMail_inRelated = true;
					}
					for ($i=0; $i<count($structure->parts); $i++) {
						$this->decodeMail_traverseStructure($structure->parts[$i]);
					}
					$decodeMail_inRelated = false;
				}
				break;
		
			default:
				$this->decodeMail_addAttachment($structure);
		}
	}

	/**
	* Private function used by the traverse function (above)
	* to add an attachment to the output object.
	*/
	function decodeMail_addAttachment($structure)
	{
		global $decodeMail_output;

		$output = &$decodeMail_output;

		// Determine filename and extension
		if (!empty($structure->d_parameters['filename'])) {
			$filename = $structure->d_parameters['filename'];
			if (preg_match('/^.+\.(\w{3,4})$/', $filename, $matches)) {
				$extension = $matches[1];
			} else {
				$extension = '';
			}
		} else if (!empty($structure->ctype_parameters["name"])) {
			$filename = $structure->ctype_parameters["name"];
			if (preg_match('/^.+\.(\w{3,4})$/', $filename, $matches)) {
				$extension = $matches[1];
			} else {
				$extension = '';
			}
		} else {
			$filename  = '';
			$extension = '';
		}
		$ctype  = strtolower(@$structure->ctype_primary) . '/' . strtolower(@$structure->ctype_secondary);
		// Attachment or embedded?
		$var = 'attachments';
		$output->{$var}[] = array('data'      => @$structure->body,
								  'size'      => @strlen($structure->body),
								  'filename'  => $filename,
								  'extension' => $extension,
								  'contenttype' => $ctype);
	}

	/**
	* Validate the Email Address
	*/
	function isValidEmail($email) {
		return (eregi( 
			'^[-!#$%&\'*+\\./0-9=?A-Z^_`{|}~]+'.      // the user name 
			'@'.                                      // the ubiquitous at-sign 
			'([-0-9A-Z]+\.)+' .                       // host, sub-, and domain names 
			'([0-9A-Z]){2,5}$',                       // top-level domain (TLD) 
			trim($email))); 
	}
}
?>